package com.ruoyi.web.core.config;

import com.fasterxml.classmate.ResolvedType;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.properties.DateProperty;
import io.swagger.models.properties.IntegerProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.StringProperty;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;

import java.util.*;

/**
 * 注册一个OperationBuilderPlugin
 */
@Component
public class ApiResponseBodyReader implements OperationBuilderPlugin {

    /**
     * swagger Modle名 的后缀
     */
    public static final String swaggerFuffix = "_Map";

    /**
     * 存储所有的 Modle名
     */
    public static List<String> extra_key = new ArrayList<>();

    /**
     * 存储所有 Modle数据
     */
    public static Map<String, Model> extra_cache = new HashMap<>();

    /**
     * 存储所有返回值类型
     */
    public static Map<String,Class<?>> resultType = new HashMap<>();

    /**
     * 自定义在map中的 swagger描述
     */
    private static Map<String, List<SwaggerMapColum>> swaggerMap = null;



    /**
     * 是否支持此类DocumentationType
     *
     * @param delimiter 当前文档类型
     * @return 如果支持当前类型，则返回true
     */
    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }

    /**
     * @param operationContext 方法上下文
     */
    @Override
    public void apply(OperationContext operationContext) {
        Optional<ApiResponseMap> optional = operationContext.findAnnotation(ApiResponseMap.class);

        //如果方法标注有@ApiResponseMap注解，并且返回不位 void 则执行下列逻辑
        if (optional.isPresent() && !isVoid(operationContext)) {
            ApiResponseMap apiResponseObject = optional.get();
            ModelImpl model = null;
            if(apiResponseObject.properties().length > 0 ) {
                model = createModel(apiResponseObject); //使用@ApiResponseProperty注解进行列描述的进入该方法
            }else {
                model = createModel_Map(apiResponseObject); //在SwaggerMap中定义列描述的，进入该方法
            }

            String modelName = apiResponseObject.modleName(); //model名
            String modelNameFuffix = modelName+swaggerFuffix;

            // Modle名
            ApiResponseBodyReader.extra_key.add(modelName);
            //存储所有Modle数据
            ApiResponseBodyReader.extra_cache.put(modelNameFuffix, model);
            //存储方法返回值类型
            setResultType(operationContext,modelName);

            addResponseMessage(operationContext, modelName);
        }
    }

    /**
     *  根据在SwaggerMap定义好的字段描述生成一个ModelImpl
     *
     * @param apiResponseObject
     * @return
     */
    private ModelImpl createModel_Map(ApiResponseMap apiResponseObject){
        if (swaggerMap == null) {
            swaggerMap = SwaggerMapObject.swaggerMap;//获取定义好的swagger字段描述
        }

        ModelImpl result = new ModelImpl();
        result.setType("object");  //类型指定object

        String modelName = apiResponseObject.modleName();
        List<SwaggerMapColum> swaggerMapColums = swaggerMap.get(modelName);

        if(swaggerMapColums != null && swaggerMapColums.size()>0) {
            for (SwaggerMapColum item : swaggerMapColums) {
                String name = item.getName();  //字段名
                String type = item.getType();  //字段类型
                String description = item.getDescription(); //字段描述
                Property property = null;
                if ("string".equalsIgnoreCase(type)) {
                    property = new StringProperty();
                } else if ("int".equalsIgnoreCase(type) || "Integer".equalsIgnoreCase(type)) {
                    property = new IntegerProperty();
                } else if ("date".equalsIgnoreCase(type)) {
                    property = new DateProperty();
                } else {
                    throw new RuntimeException("@ApiResponseMap注解未支持的类型");
                }
                property.setDescription(description);
                result.property(name, property);
            }
        }
        return result;
    }


    /**
     * 根据注解生成一个ModelImpl
     *
     * @param apiResponseObject
     * @return
     */
    private ModelImpl createModel(ApiResponseMap apiResponseObject) {
        ModelImpl result = new ModelImpl();
        //类型指定object
        result.setType("object");
        ApiResponseProperty[] properties = apiResponseObject.properties();
        for (ApiResponseProperty apiResponseProperty : properties) {
            String name = apiResponseProperty.name();
            String description = apiResponseProperty.description();
            String type = apiResponseProperty.type();
            Property property = null;
            if ("string".equalsIgnoreCase(type)) {
                property = new StringProperty();
            } else if ("int".equalsIgnoreCase(type) || "Integer".equalsIgnoreCase(type)) {
                property = new IntegerProperty();
            } else if ("date".equalsIgnoreCase(type)) {
                property = new DateProperty();
            } else {
                throw new RuntimeException("@ApiResponseMap注解未支持的类型");
            }
            property.setDescription(description);
            result.property(name, property);
        }
        return result;
    }

    /**
     * 为operationContext添加状态为200的返ResponseMessage
     *
     * @param operationContext
     * @param typeName
     */
    private void addResponseMessage(OperationContext operationContext, String typeName) {
        ResponseMessage responseMessage = new ResponseMessageBuilder()
                .code(200).responseModel(new ModelRef(typeName))
                .build();
        HashSet<ResponseMessage> responseMessages = new HashSet<>();
        responseMessages.add(responseMessage);
        operationContext.operationBuilder().responseMessages(responseMessages);
    }

    private boolean isVoid(OperationContext operationContext) {
        ResolvedType type = operationContext.getReturnType();
        Class<?> aClass = type.getErasedType();
        return aClass == void.class;
    }

    private void setResultType(OperationContext operationContext, String modelName) {
        ResolvedType type = operationContext.getReturnType();
        Class<?> aClass = type.getErasedType();
        resultType.put(modelName,aClass);
    }

}

